package com.evolved.automata.experimental;

import java.io.BufferedReader;
import java.io.BufferedWriter;

import com.evolved.automata.experimental.UtilityAndPerferenceModule.Utility;


public class AgentInfo implements Serializer
{
	
	private int j_Fitness;
	private Agent j_Agent;
	private boolean j_PausedP;
	private SensoryMotorDataProvider j_Provider;
	private int j_TotalLifeSpan;
	private int j_CriticalNegativeThreshold;
	private int j_EvalutationInterval;
	private int j_FitnessCounter=0;
	private int j_NegativeUtilityCounter=0;
	
	private boolean j_NaturalDeathP;
	private boolean j_KilledP;
	
	public AgentInfo()
	{
		j_SerializerHelper = new SerializerAdapter(this);
	}
	
	public AgentInfo(SensoryMotorDataProvider provider, Agent theAgent)
	{
		j_SerializerHelper = new SerializerAdapter(this);
		j_EvalutationInterval=provider.GetLifeStepSize();
		j_CriticalNegativeThreshold=provider.GetCriticalNegUtilityThreshold();
		j_TotalLifeSpan=provider.GetMaxLifeSpan();
		j_Provider=provider;
		j_Fitness=0;
		j_Agent=theAgent;
		j_PausedP=false;
	}
	
	public void Pause()
	{
		j_PausedP=true;
	}
	
	public void Continue()
	{
		j_PausedP=false;
	}
	
	public void ResetFitness(int newValue)
	{
		j_Fitness=newValue;
		j_FitnessCounter=0;
		j_NegativeUtilityCounter=0;
		j_KilledP=j_NaturalDeathP=false;
	}
	public boolean Update(UtilityAndPerferenceModule.Utility utility)
	{
		if (j_KilledP||j_NaturalDeathP)
			return false;
		
		j_FitnessCounter++;
		if (utility == Utility.NEGATIVE)
		{
			j_NegativeUtilityCounter++;
			if (j_NegativeUtilityCounter>j_CriticalNegativeThreshold)
			{
				j_KilledP=true;
				return false;
			}
			
		}
		if (j_FitnessCounter==j_EvalutationInterval)
		{
			j_FitnessCounter=0;
			j_Fitness++;
			j_NegativeUtilityCounter=0;
		}
		
		if (j_Fitness>=j_TotalLifeSpan)
			j_NaturalDeathP=true;
		
		return j_NaturalDeathP;
	}
	
	public boolean AliveP()
	{
		return (!j_PausedP)&&!j_NaturalDeathP&&!j_KilledP;
	}
	
	public boolean PausedP()
	{
		return j_PausedP;
	}
	
	public boolean SuccessfulFinish()
	{
		return j_NaturalDeathP;
	}
	
	public boolean KilledP()
	{
		return j_KilledP;
	}
	
	public Agent GetAgent()
	{
		return j_Agent;
	}
	
	public void UpdateAgent()
	{
		
		int[] state = j_Provider.GetSensoryData();
		
		Integer[] returnValue = j_Agent.ExecuteDecisionProcess(state, j_Provider.GetActionOptions());
		
		if (returnValue!=null)
		{
			int[] a = new int[returnValue.length];
			int j=0;
			
			for (Integer i:returnValue)
			{
				a[j]=i;
				j++;
			}
			j_Provider.ApplyAction(a);          
		}
	}
	
	public int GetFitness()
	{
		return j_Fitness;
	}
	
	
	
	
	/**********************************************
	 * Serializer Methods and Fields
	 ********************************************** 
	 */
	

	
	private enum LoadState
	{
		LOADING_FITNESS_PARAMETERS,
		LOADING_BOOLEANS,
		LOADING_PROVIDER_TYPE,
		LOADING_AGENT
	}
	
	private enum SaveState
	{
		SAVING_FITNESS_PARAMETERS,
		SAVING_BOOLEANS,
		SAVING_PROVIDER_TYPE,
		SAVING_AGENT,
		EXIT
	}
	
	/**********************
	 * End of Serializer-specific Fields 
	 **********************
	 */
	
	private String j_UniqueName;
	private SerializerAdapter j_SerializerHelper;
	private LoadState j_CurrentState;
	
	
	/* AgentInfo-Specific Serializer methods
	 *
	 */
	
	/* TODO: Set this to save the data.
	 */
	public void SaveData(BufferedWriter writer) {
		SaveState currentState=SaveState.SAVING_FITNESS_PARAMETERS;
		// Must traverse save state machine in same basic order as load machine is expected to be traversed
		while (currentState!=SaveState.EXIT)
		{
			switch (currentState)
			{
				case SAVING_FITNESS_PARAMETERS:
					// Save 
					int[] fParams = new int[]{j_Fitness, j_FitnessCounter, j_NegativeUtilityCounter};
					StaticTools.WriteArray(writer, fParams);
					currentState = SaveState.SAVING_BOOLEANS; 
					break;
				case SAVING_BOOLEANS:
					boolean[] bParams = new boolean[]{j_KilledP, j_NaturalDeathP, j_PausedP};
					StaticTools.WriteArray(writer, bParams);
					currentState = SaveState.SAVING_PROVIDER_TYPE; 
					break;
				case SAVING_PROVIDER_TYPE:
					StaticTools.WriteRawData(writer, j_Provider.GetTypeName());
					currentState = SaveState.SAVING_AGENT; 
					break;
				case SAVING_AGENT:
					String name = j_Agent.GetTypeName();
					j_Agent.SetName(j_SerializerHelper.ConstructUniqueName(name,"0"));
					j_Agent.Save(writer);
					currentState = SaveState.EXIT;
					break;
			}
		}

	}
	
	
	/*
	 * Use this to load simple data values
	 */
	public void AddData(String dataLine) {
		// Must traverse load state machine in same basic order as data is saved in SaveData
		switch (j_CurrentState)
		{
			case LOADING_FITNESS_PARAMETERS:
				StaticTools.ParseIntStringValues(dataLine, 
					new StaticTools.SimpleDataParser()
					{
						public void AddBoolean(boolean data, int index) {
							// TODO Auto-generated method stub
							
						}

						public void AddInteger(int data, int index) {
							// TODO Auto-generated method stub
							switch (index)
							{
								case 0:
									j_Fitness=data;
									break;
								case 1:
									j_FitnessCounter=data;
									break;
								case 2:
									j_NegativeUtilityCounter=data;
									break;
							}
						}
						
					}
				);
				j_CurrentState=LoadState.LOADING_BOOLEANS;
				break;
			case LOADING_BOOLEANS:
				StaticTools.ParseBooleanStringValues(dataLine, 
						new StaticTools.SimpleDataParser()
						{
							public void AddBoolean(boolean data, int index) {
								
								switch (index)
								{
									case 0:
										j_KilledP=data;
										break;
									case 1:
										j_NaturalDeathP=data;
										break;
									case 2:
										j_PausedP=data;
										break;
								}
							}

							public void AddInteger(int data, int index) {
								
							}
							
						}
					);
				j_CurrentState=LoadState.LOADING_PROVIDER_TYPE;
				break;
			case LOADING_PROVIDER_TYPE:
				j_Provider = StaticTools.GetTypeFromName(dataLine);
				j_CurrentState=LoadState.LOADING_AGENT;
				break;
			case LOADING_AGENT:
				// do nothing, this method is not supposed to be called in this state
				break;
		}
	}

	

	
	
	/* 
	 * Use this to add Serializers
	 */
	public void AddData(Serializer object, String instanceId) {
		// Must traverse load state machine in same basic order as data is saved in SaveData
		switch (j_CurrentState)
		{
		case LOADING_FITNESS_PARAMETERS:
			// do nothing, this method is not supposed to be called in this state
			break;
		case LOADING_BOOLEANS:
			// do nothing, this method is not supposed to be called in this state
			break;
		case LOADING_PROVIDER_TYPE:
			// do nothing, this method is not supposed to be called in this state
			break;
		case LOADING_AGENT:
			j_Agent = (Agent)object;
			j_Agent.UpdateBaseInferentialFreedoms(j_Provider.GetBaseSensoryInferentialFreedom());
			break;
		}
	}

	
	/* 
	 * TODO: Set this to the String value that will be used to create this object in the StaticTools.GetSerializer() class 
	 */
	
	public String GetTypeName() {
		
		return "AgentInfo";
	}

	/* 
	 * @see com.evolved.automata.experimental.Serializer#PrepareSerialization()
	 */
	
	public void PrepareSerialization() {
		// TODO Uncomment the code below after the current state has been set to first load state  
		j_CurrentState=LoadState.LOADING_FITNESS_PARAMETERS;
	}
	
	
	
	
	
	/*
	 * 
	 * Boiler-plate code from SerializerTemplate
	 */
	
	public void Load(BufferedReader reader)
	{
		j_SerializerHelper.Load(reader);
	}
	
	
	public void Load(String fileFullName)
	{
		j_SerializerHelper.Load(fileFullName);
		
	}



	public void Save(String fileFullName)
	{
		j_SerializerHelper.Save(fileFullName);
	}


	public  void Save(BufferedWriter writer)
	{
		j_SerializerHelper.Save(writer);
	}

	
	public void SetName(String uniqueName)
	{
		j_UniqueName=uniqueName;
		j_SerializerHelper.SetUniqueName(uniqueName);
	}
	
	
	
}